﻿using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Developer.Core.FrameWork.RedisExtensions.RedisSubscribeInit.Cache
{
    public class RedisCacheService : ICacheService
    {
        private int Default_Timeout;
        private JsonSerializerSettings jsonConfig;
        private ConnectionMultiplexer connectionMultiplexer;
        private IDatabase database;

        public RedisCacheService(ConfigurationOptions options) : base()
        {
            JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
            serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            serializerSettings.NullValueHandling = NullValueHandling.Ignore;
            this.jsonConfig = serializerSettings;
            ThreadPool.SetMinThreads(200, 200);
            this.connectionMultiplexer = ConnectionMultiplexer.Connect(options);
            this.database = this.connectionMultiplexer.GetDatabase(-1, (object)null);
        }

        public RedisCacheService(string configuration) : base()
        {
            JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
            serializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
            serializerSettings.NullValueHandling = NullValueHandling.Ignore;
            this.jsonConfig = serializerSettings;

            ThreadPool.SetMinThreads(200, 200);
            this.connectionMultiplexer = ConnectionMultiplexer.Connect(configuration);
            this.database = this.connectionMultiplexer.GetDatabase(-1, (object)null);
        }

        public ConnectionMultiplexer GetRedis() => this.connectionMultiplexer;

        public IDatabase GetDatabase() => this.database;

        public int TimeOut
        {
            get => this.Default_Timeout;
            set => this.Default_Timeout = value;
        }

        public string Get(string key) => (string)this.database.StringGet((RedisKey)key);

        public T Get<T>(string key) => JsonConvert.DeserializeObject<T>((string)this.database.StringGet((RedisKey)key));

        public void Set(string key, object data) => this.database.StringSet((RedisKey)key, (RedisValue)JsonConvert.SerializeObject(data));

        public void Set(string key, object data, int cacheTime)
        {
            TimeSpan timeSpan = TimeSpan.FromSeconds((double)cacheTime);
            this.database.StringSet((RedisKey)key, (RedisValue)JsonConvert.SerializeObject(data), new TimeSpan?(timeSpan));
        }

        public void Remove(string key) => this.database.KeyDelete((RedisKey)key, CommandFlags.HighPriority);

        public bool Exists(string key) => this.database.KeyExists((RedisKey)key);

        public string[] ScriptEvaluateKeys(string key) => (string[])this.database.ScriptEvaluate(LuaScript.Prepare(" local res = redis.call('KEYS', @keypattern)  return res "), (object)new
        {
            keypattern = key
        });

        public long ListLeftPush(string key, string value) => this.database.ListLeftPush((RedisKey)key, (RedisValue)value);

        public async Task<long> ListLeftPushAsync(string key, string value) => await this.database.ListLeftPushAsync((RedisKey)key, (RedisValue)value);

        public long ListRightPush(string key, string value) => this.database.ListRightPush((RedisKey)key, (RedisValue)value);

        public async Task<long> ListRightPushAsync(string key, string value) => await this.database.ListRightPushAsync((RedisKey)key, (RedisValue)value);

        public T ListLeftPop<T>(string key) where T : class
        {
            RedisValue redisValue = this.database.ListLeftPop((RedisKey)key);
            return string.IsNullOrEmpty((string)redisValue) ? default(T) : JsonConvert.DeserializeObject<T>((string)redisValue);
        }

        public async Task<T> ListLeftPopAsync<T>(string key) where T : class
        {
            RedisValue redisValue = await this.database.ListLeftPopAsync((RedisKey)key);
            return !string.IsNullOrEmpty((string)redisValue) ? (T)JsonConvert.DeserializeObject<T>((string)redisValue) : default(T);
        }

        public T ListRightPop<T>(string key) where T : class
        {
            RedisValue redisValue = this.database.ListRightPop((RedisKey)key);
            return string.IsNullOrEmpty((string)redisValue) ? default(T) : JsonConvert.DeserializeObject<T>((string)redisValue);
        }

        public async Task<T> ListRightPopAsync<T>(string key) where T : class
        {
            RedisValue redisValue = await this.database.ListRightPopAsync((RedisKey)key);
            return !string.IsNullOrEmpty((string)redisValue) ? (T)JsonConvert.DeserializeObject<T>((string)redisValue) : default(T);
        }

        public string ListLeftPop(string key) => (string)this.database.ListLeftPop((RedisKey)key);

        public async Task<string> ListLeftPopAsync(string key) => (string)await this.database.ListLeftPopAsync((RedisKey)key);

        public string ListRightPop(string key) => (string)this.database.ListRightPop((RedisKey)key);

        public async Task<string> ListRightPopAsync(string key) => (string)await this.database.ListRightPopAsync((RedisKey)key);

        public long ListLength(string key) => this.database.ListLength((RedisKey)key);

        public async Task<long> ListLengthAsync(string key) => await this.database.ListLengthAsync((RedisKey)key);

        private class CacheObject<T>
        {
            public int ExpireTime { get; set; }

            public bool ForceOutofDate { get; set; }

            public T Value { get; set; }
        }
    }
}
